# -*- coding: utf-8 -*-

"""
@author: bw
@contact: bw@handsdriver.net
"""

__all__ = ('VagrantMachine',)

import logging

import plumbum.cmd
import plumbum.commands      as _plmcmd
import plumbum.machines.base as _plmbase
import plumbum.path          as _plmpath

from . import command as _vgcmd
from . import environ as _vgenv
from . import path    as _vgpath
from . import session as _vgsess


logger = logging.getLogger('plumbum.vagrant')


class VagrantMachine(_plmbase.BaseMachine):
	custom_encoding = 'utf-8'  # ??? sys.getdefaultencoding() / sys.getfilesystemencoding()
	sep     = '/'
	pathsep = ':'

	uname = property(lambda self: self.__uname)
	cwd   = property(lambda self: self.__cwd)
	env   = property(lambda self: self.__env)

	@cwd.setter
	def cwd(self, value):
		self.__cwd.change(value)

	def __init__(self, vmdir=None, vmname=None):
		self.vm_dir  = vmdir or os.getcwd()
		self.vm_name = vmname

		self.sessions = []
		self.newSession()

		self.__uname = self.__get_uname()
		self.__cwd   = _vgpath.VagrantWorkdir(self)
		self.__env   = _vgenv.VagrantEnviron(self)

		self.__env.update(self.getSession().current_env)

	def __enter__(self):
		return self

	def __exit__(self, etype, evalue, tb):
		self.close()

	def __getitem__(self, cmd):
		if isinstance(cmd, _vgpath.VagrantPath):
			if cmd.machine is self:
				return _vgcmd.VagrantCommand(self, cmd)
			raise TypeError('Given path does not belong to this remote machine: {!}r'.format(cmd))

		if type(cmd) is not str:
			raise TypeError('Expected <str>', type(cmd))

		if self.sep in cmd:  # @xxx: эскапирование и квотирование не учитываются
			return _vgcmd.VagrantCommand(self, self.path(cmd))

		return _vgcmd.VagrantCommand(self, self.which(cmd))

	def close(self):
		# @todo: (?) signal.alarm(...)

		while self.sessions:
			session = self.sessions.pop()
			session.close()

	def getSession(self):
		while self.sessions:
			for session in self.sessions:
				if not session.alive():
					self.sessions.remove(session)
					break
				if session.broken():
					session.close()
					self.sessions.remove(session)
					break
				if not session.busy:
					return session
			else:
				break
		return self.newSession()

	def newSession(self):
		args  = ('ssh', '--no-tty')
		if self.vm_name:
			args += (self.vm_name,)
		args += ('--command', 'sh')
		proc = plumbum.cmd.vagrant[args].popen(cwd=self.vm_dir)

		session = _vgsess.VagrantSession(proc)
		self.sessions.append(session)

		# @note: по поводу `cwd` и `env` см. L{VagrantCommand.popen}
		logger.debug('New session: %r', session)
		return session

#	@property
#	def python(self): ...
#	python = LocalCommand(sys.executable, custom_encoding)

#	def session(self, isatty=False, new_session=False):
#	def download(self, src, dst):
#	def upload(self, src, dst):
#	def popen(self, args, **kwargs):

	def daemonic_popen(self, command, cwd='/', stdout=None, stderr=None, append=True):
		raise NotImplementedError('VagrantMachine.daemonic_popen', command, cwd, stdout, stderr, append)

	def path(self, *parts):
		result = [str(self.cwd)]

		for part in parts:
			if isinstance(part, _plmpath.LocalPath):
				raise TypeError('Cannot construct VagrantPath from {!r}'.format(part))
			result.append(self.env.expanduser(str(part)))

		return _vgpath.VagrantPath(self, *result)

	def which(self, progname):
#		alternatives = [progname]
#		if '_' in progname:
#			alternatives.append(progname.replace('_', '-'))
#			alternatives.append(progname.replace('_', '.'))
#		for name in alternatives:

		for path in self.env.path:
			fn = path / progname
			if fn.access('x') and not fn.is_dir():
				return fn

		raise _plmcmd.CommandNotFound(progname, self.env.path)

#	def list_processes(self):
#	def pgrep(self, pattern):
#	@contextmanager
#	def tempdir(self):
#	@_setdoc(BaseEnv)
#	def expand(self, expr):
#	@_setdoc(BaseEnv)
#	def expanduser(self, expr):

	def __get_uname(self):
		rc, out, _ = self.getSession().run('uname', retcode=None)

		if rc == 0:
			return out.strip()

		return 'Unknown'
